#!/bin/python
import argparse
import hashlib
import json
import logging
import os
import sys


def cleanup_json(data):
    """Cleans up the json structure by removing empty "", and empty key value
    pairs."""
    if (isinstance(data, unicode)):
        copy = data.strip()
        return None if len(copy) == 0 else copy

    if (isinstance(data, dict)):
        copy = {}
        for key, value in data.iteritems():
            rem = cleanup_json(value)
            if (rem is not None):
                copy[key] = rem
        return None if len(copy) == 0 else copy

    if (isinstance(data, list)):
        copy = []
        for elem in data:
            rem = cleanup_json(elem)
            if (rem is not None):
                if rem not in copy:
                    copy.append(rem)

        if len(copy) == 0:
            return None
        return copy


class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

    def as_list(self, name):
        v = self.get(name, [])
        if (isinstance(v, list)):
            return v

        return [v]


def remove_lib_prefix(module):
    """Removes the lib prefix, as we are not using them in CMake."""
    if module.startswith('lib'):
        return module[3:]
    else:
        return module


def escape(msg):
    """Escapes the "."""
    return '"' + msg.replace('"', '\\"') + '"'


def header():
    """The auto generate header."""
    return [
        '# This is an autogenerated file! Do not edit!',
        '# instead run make from .../device/generic/goldfish-opengl',
        '# which will re-generate this file.'
    ]


def checksum(fname):
    """Calculates a SHA256 digest of the given file name."""
    m = hashlib.sha256()
    with open(fname, 'r') as mk:
        m.update(mk.read())
    return m.hexdigest()


def generate_module(module):
    """Generates a cmake module."""
    name = remove_lib_prefix(module['module'])
    make = header()
    mkfile = os.path.join(module['path'], 'Android.mk')
    sha256 = checksum(mkfile)
    make.append(
        'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256))
    make.append('set(%s_src %s)' % (name, ' '.join(module['src'])))
    if module['type'] == 'SHARED_LIBRARY':
        make.append('android_add_library(TARGET {} SHARED LICENSE Apache-2.0 SRC {})'.format(name, ' '.join(module['src'])))
    elif module['type'] == 'STATIC_LIBRARY':
        make.append('android_add_library(TARGET {} LICENSE Apache-2.0 SRC {})'.format(name, ' '.join(module['src'])))
    else:
        raise ValueError('Unexpected module type: %s' % module['type'])

    # Fix up the includes.
    includes = ['${GOLDFISH_DEVICE_ROOT}/' + s for s in module['includes']]
    make.append('target_include_directories(%s PRIVATE %s)' %
                (name, ' '.join(includes)))

    # filter out definitions
    defs = [escape(d) for d in module['cflags'] if d.startswith('-D')]

    #  And the remaining flags.
    flags = [escape(d) for d in module['cflags'] if not d.startswith('-D')]

    # Make sure we remove the lib prefix from all our dependencies.
    libs = [remove_lib_prefix(l) for l in module.get('libs', [])]
    staticlibs = [remove_lib_prefix(l) for l in
                      module.get('staticlibs', [])
                      if l != "libandroidemu"]

    # Configure the target.
    make.append('target_compile_definitions(%s PRIVATE %s)' %
                (name, ' '.join(defs)))
    make.append('target_compile_options(%s PRIVATE %s)' %
                (name, ' '.join(flags)))

    if len(staticlibs) > 0:
        make.append('target_link_libraries(%s PRIVATE %s PRIVATE %s)' %
                    (name, ' '.join(libs), " ".join(staticlibs)))
    else:
        make.append('target_link_libraries(%s PRIVATE %s)' %
                    (name, ' '.join(libs)))
    return make


def main(argv=None):
    parser = argparse.ArgumentParser(
        description='Generates a set of cmake files'
        'based up the js representation.'
        'Use this to generate cmake files that can be consumed by the emulator build')
    parser.add_argument('-i', '--input', dest='input', type=str, required=True,
                        help='json file containing the build tree')
    parser.add_argument('-v', '--verbose',
                        action='store_const', dest='loglevel',
                        const=logging.INFO, default=logging.ERROR,
                        help='Log what is happening')
    parser.add_argument('-o', '--output',
                        dest='outdir', type=str, default=None,
                        help='Output directory for create CMakefile.txt')
    parser.add_argument('-c', '--clean', dest='output', type=str,
                        default=None,
                        help='Write out the cleaned up js')
    args = parser.parse_args()

    logging.basicConfig(level=args.loglevel)

    with open(args.input) as data_file:
        data = json.load(data_file)

    modules = cleanup_json(data)

    # Write out cleaned up json, mainly useful for debugging etc.
    if (args.output is not None):
        with open(args.output, 'w') as out_file:
            out_file.write(json.dumps(modules, indent=2))

    # Location --> CMakeLists.txt
    cmake = {}

    # The root, it will basically just include all the generated files.
    root = os.path.join(args.outdir, 'CMakeLists.txt')
    mkfile = os.path.join(args.outdir, 'Android.mk')
    sha256 = checksum(mkfile)
    cmake[root] = header()
    cmake[root].append('set(GOLDFISH_DEVICE_ROOT ${CMAKE_CURRENT_SOURCE_DIR})')
    cmake[root].append(
        'android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/%s" "%s")' % (mkfile, sha256))

    # Generate the modules.
    for module in modules:
        location = os.path.join(args.outdir, module['path'], 'CMakeLists.txt')

        # Make sure we handle the case where we have >2 modules in the same dir.
        if location not in cmake:
            cmake[root].append('add_subdirectory(%s)' % module['path'])
            cmake[location] = []
        cmake[location].extend(generate_module(module))

    # Write them to disk.
    for (loc, cmklist) in cmake.iteritems():
        logging.info('Writing to %s', loc)
        with open(loc, 'w') as fn:
            fn.write('\n'.join(cmklist))


if __name__ == '__main__':
    sys.exit(main())
